/*
 * Controller.c
 *
 *  Created on: 09.07.2011
 *      Author: sili
 */

#include "Controller.h"

#include "AALItem.h"
#include "AALLocatorItem.h"
#include "AALSensorItem.h"
#include "AALActorItem.h"

#include "SystemFactory.h"
#include "TemplateFactory.h"
#include "PropertyFactory.h"
#include "VisFactory.h"
#include "AudioFactory.h"
#include "LogFactory.h"

#include "HttpServer.h"
#include "HttpClient.h"

static log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("at.sili.aalsystem.controller.Controller"));

static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

/**
 * Class member function always need an implementation in VDR....
 */
void ActionListener::doAction(AALAction * _action){}
void ActionListener::doMainSavedAction(){}
void ActionListener::discardAction(AALAction * _action){}

Controller * Controller::instance = 0;

Controller * Controller::getInstance(){
	if( instance == 0 )
		instance = new Controller();
	return instance;
}

Controller::Controller() {
		this->systemFactory=NULL;
		this->templateFactory=NULL;
		this->propFactory=NULL;
		this->visFactory=NULL;
		this->audioFactory=NULL;

		this->httpServer=NULL;
		this->httpClient=NULL;

		this->logFactory=NULL;
}

Controller::~Controller() {
	if(this->systemFactory)
		delete(this->systemFactory);
	if(this->templateFactory)
		delete(this->templateFactory);
	if(this->propFactory)
		delete(this->propFactory);
	if(this->visFactory)
		delete(this->visFactory);
	if(this->audioFactory)
		delete(this->audioFactory);

	if(this->httpServer)
		delete(this->httpServer);
	if(this->httpClient)
		delete(this->httpClient);

	if(this->logFactory)
		delete(this->logFactory);
	XMLPlatformUtils::Terminate();
}

void Controller::init(const char * _configDir){
	try {
		XMLPlatformUtils::Initialize();
	} catch (const XMLException& toCatch) {
		char* message = XMLString::transcode(toCatch.getMessage());
		AAL_ERROR(logger, "Exception while initializing XMLPlatformUtils. Error message is: %s", message);
		XMLString::release(&message);
	}

	this->logFactory = new LogFactory(this, _configDir);
	this->systemFactory = new SystemFactory(this, _configDir);
	this->templateFactory = new TemplateFactory(this, _configDir);
	this->propFactory = new PropertyFactory(this, _configDir);
	this->visFactory = new VisFactory(this);
	this->audioFactory = new AudioFactory(this, _configDir);

	this->httpServer = new HttpServer(this, _configDir);
	this->httpClient = new HttpClient(this);
}

void Controller::addListener(ActionListener * _listener){
	this->listeners.push_back(_listener);
}

AALItem * Controller::getAnAALItem(){
	AALItem * _aalItem = getRootAALItem();

	int defaultFlatId = this->getPropertyFactory()->getIntProperty(AALPROP_DEFAULTFLAT,-1);
	AALItem * tmp = getRootAALItem()->getAALItem(defaultFlatId);
	if (tmp != NULL) {
		_aalItem = tmp;
		AAL_DEBUG(logger, "Using defaultFlat (aalItem) %d", _aalItem->getId());
	} else {
		AAL_DEBUG(logger, "DefaultFlatIdProp doesn't lead to an valid aalItem. -> Ignoring and using rootAALItem");
	}
	return _aalItem;
}


void Controller::publish(ActionType _actionType, const char * _message, AALItem * _aalItem){
	if(_aalItem == NULL){
		_aalItem = getAnAALItem();
	}
	AALAction newAction(_actionType, _aalItem, _message, 0, this);
	publish(&newAction);
}

void Controller::publish(AALAction * _action){
	pthread_rwlock_rdlock(&rwlock);

	if(_action->aalItem == NULL){
		_action->aalItem = getAnAALItem();
	}

	AAL_DEBUG(logger, "Publishing action id:%ld type:%s message:\"%s\" aalItem:%d", _action->id, _action->typeToString(), _action->message.c_str(), _action->aalItem != NULL ? _action->aalItem->getId():-1);
	vector<ActionListener *>::iterator entry;
	for (entry=this->listeners.begin(); entry<this->listeners.end(); entry++ ){
		ActionListener * child = (*entry);
		AAL_TRACE(logger, "Publishing to %s", child->getName());
		child->doAction(_action);
	}
	pthread_rwlock_unlock(&rwlock);
}

void Controller::discard(AALAction * _action){
	pthread_rwlock_rdlock(&rwlock);
	AAL_DEBUG(logger, "Discarding action id:%ld type:%s message:\"%s\" aalItem:%d", _action->id, _action->typeToString(), _action->message.c_str(), _action->aalItem != NULL ? _action->aalItem->getId():-1);
	vector<ActionListener *>::iterator entry;
	for (entry=this->listeners.begin(); entry<this->listeners.end(); entry++ ){
		ActionListener * child = (*entry);
		AAL_TRACE(logger, "Discarding to %s", child->getName());
		child->discardAction(_action);
	}
	pthread_rwlock_unlock(&rwlock);
}


void Controller::executeActionsInMainThread(){
	pthread_rwlock_rdlock(&rwlock);
	vector<ActionListener *>::iterator entry;
	for (entry=this->listeners.begin(); entry<this->listeners.end(); entry++ ){
		ActionListener * child = (*entry);
		child->doMainSavedAction();
	}
	pthread_rwlock_unlock(&rwlock);
}



cOsdObject * Controller::mainMenuAction(){
  return this->visFactory->mainMenuAction();
}

TemplateFactory * Controller::getTemplateFactory(){
	return this->templateFactory;
}

PropertyFactory * Controller::getPropertyFactory(){
	return this->propFactory;
}

LogFactory * Controller::getLogFactory(){
	return this->logFactory;
}

AudioFactory * Controller::getAudioFactory(){
	return this->audioFactory;
}

SystemFactory * Controller::getSystemFactory(){
	return this->systemFactory;
}

AALItem * Controller::getRootAALItem(){
	return this->systemFactory->getSystem();
}

const char * Controller::getConfigDir(){
	return this->systemFactory->getConfigDir();
}

AALItem * Controller::createAALItem(const char * _type, int _itemId){
	if(strcmp("locator",_type) == 0){
		return new AALLocatorItem(_itemId, this);
	} else if(strcmp("sensor",_type) == 0){
		return new AALSensorItem(_itemId, this);
	} else if(strcmp("actuator",_type) == 0){
		return new AALActorItem(_itemId, this);
	} else {
		return new AALItem(_itemId, this);
	}
}

const char * logFormat(const char * szTypes, ...){
	va_list va;
	static char formatted[1024];
	va_start(va, szTypes);
	vsnprintf(formatted, 1024, szTypes, va);
	va_end(va);
	//Controller::getInstance()->getLogFactory()->newLogMessage(warn,formatted);
	return formatted;
}
